MacでJDK6以降のstdoutがSJISになる現象の根源
概要
Tさんに粘着して教えていただいたので、纏めておく。
Tさんすいませんでした。
参考
http://happygiraffe.net/blog/2009/09/24/java-platform-encoding/
参考の意訳
不運にも、コマンドラインでJavaを実行する際、UTF-8文字列(U+00A9 COPYRIGHT SIGN[?])を渡したものをprintしたら、二重にエンコードされてしまった。
とりあえず-Dfile.encoding=UTF-8で対処していたんだけど、
自動リスタートとかしてたら、反映されない。これは不味い。
で、私たちは問題をさらに調査した。
-Dつけてれば適応されるんだし、きっとLANG環境変数が欠如している事と関係があるだろうな、とあたりをつけた。
試しに、システムプロパティ一覧を表示するアプリケーションを作って、結果をgrepしてみた。
おなじみのfile.encodingがある。さらに内部に、sun.jnu.encodingっていうのが有るのを見つけた。
で、-Dfile.encodingをつけての挙動を試してみた。
sun.jnu.encoding が変わってない!
このプロパティに関するドキュメントは、探してみたけど知る限りどこにも無いみたいだ。
実際の挙動を知る為に、JDK6のソースコードを読むしか無い。
main関数はjava.cにあった。実際に使われているJavaMain()関数の中身をみると、
NewPlatformStringArray()関数が定義されていて、コマンドラインからの入力行ごとに実行されている。
その中では、 new String(byte[], encoding)が呼ばれている。
さらにその中で呼ばれている getPlatformEncoding() 関数で、
System.getProperty("sun.jnu.encoding")
が呼ばれている。
このプロパティはどこでセットされているんだ?
System.cだ。
その中の Java_java_lang_System_initProperties() 関数の中で、
PUTPROP(props, "sun.jnu.encoding", sprops->sun_jnu_encoding);
という行があり、
sprops-> sun_jnu_encodingは、 java_props_md.c ファイル中の、
GetJavaProperties() 関数でセットされている。
こいつは、いろいろな環境変数を含んでいる。ロケールをコントロールするものも含めて。
「入力されるいろんなもの」から、sun_jnu_encodingを取得する為に設定されているみたいだ。
ちぇっ。
判った事として、次のパラメータが、 sun_jnu_encoding の定義に使われる。
Command line 引数
メインクラス名
環境変数
で、このパラメータはoverrideできる。
$ LANG= java -Dsun.jnu.encoding=UTF-8 -Dfile.encoding=UTF-8
訳終わり
という訳で
初期エンコーディングの可能性は、この java_props_md.c らへんを見れば判る、と。
GetJavaProperties メソッドの所には、
"Determine the language, country, variant, and encoding from the host"
って書いてある。これが元か。JDK5との比較しなきゃだけど、なぜこんな仕様になったんだろう。
インストーラの仕事サボった?
上からファイル追っていくと、sun.jnu.encodingの候補は、環境とかlocaleとかによってころころと変わっていく。
最終的に sun.jnu.encoding は std_encoding の値を代入されるのだけれど、
std_encoding の値が下記のように変わる。
ISO8859-15 ←これはUS-ASCII
nl_langinfo(CODESET) ←こいつは langinfo.h / langinfo.c で定義されてる。 CODESETは環境によって異なる。
#ifdef __linux__
#define CODESET _NL_CTYPE_CODESET_NAME
#else
#ifdef ALT_CODESET_KEY
#define CODESET ALT_CODESET_KEY
#endif
#endif
ISO646-US ←下記URLに色々載ってる。
http://www.asahi-net.or.jp/~yw3t-trns/code/iso646.html
ISO8859-1 ←Latin-1
さらに各環境の場合、下記がセットされる。
//en_UK だったら
ISO8859-1
//linuxだったら系
EUC-JP-LINUX
eucJP-open
Big5_Solaris
で!
langinfo.c を探してみると、
すいませんよくわかりません。どこに有るんや。
下記適当に見つけてきた langinfo.c だと、localeがjpだったらSJIS返してる。
http://www.cl.cam.ac.uk/~mgk25/ucs/langinfo.c
続く。